﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace OpenCompiler.CodeGen.Implementation
{
	public class MethodBodyBuilder : IMethodBodyBuilder
	{
		protected List<Expression> _body;
		protected Expression _returnStatement;


		internal MethodBodyBuilder(IMethodBuilder methodBuilder, MethodBodyContext context)
		{
			this._body = new List<Expression>();
			this.Context = context;
			this.MethodBuilder = methodBuilder;
		}


		public IMethodBodyBuilder Statement(Expression<Action> statement)
		{
			this._body.Add(statement);
			return this;
		}

		public IMethodBodyBuilder ReturnStatement<TResult>(Expression<Func<TResult>> statement)
		{
			this._returnStatement = statement;
			return this;
		}

		public Expression BodyWithoutReturnStatement
		{
			get
			{
				Expression composedExpression = null;
				if (this._body.Count > 0)
				{
					composedExpression = this._body[0];
					var isFirst = true;
					foreach (var expression in this._body)
					{
						if (isFirst)
						{
							isFirst = false;
							continue;
						}
						composedExpression = Expression.And(composedExpression, expression);
					}
				}

				return composedExpression;
			}
		}

		public Expression Body
		{
			get
			{
				var composedExpression = this.BodyWithoutReturnStatement;
				if (null == composedExpression)
				{
					composedExpression = this._returnStatement;
				}
				else
				{
					composedExpression = Expression.And(composedExpression, this._returnStatement);
				}

				return composedExpression;
			}
		}

		public Expression ReturnStatementBody
		{
			get
			{
				return this._returnStatement;
			}
		}

		public MethodBodyContext Context { get; protected set; }
		public IMethodBuilder MethodBuilder { get; protected set; }
	}

	public class MethodBodyBuilder<T> : MethodBodyBuilder, IMethodBodyBuilder<T>
		where T : class
	{

		internal MethodBodyBuilder(IMethodBuilder methodBuilder, MethodBodyContext context)
			: base(methodBuilder, context)
		{

		}


		public IMethodBodyBuilder<T> Statement(Expression<Action<T>> statement)
		{
			this._body.Add(statement);
			return this;
		}
	}

	public class MethodBodyBuilder<T, E> : MethodBodyBuilder, IMethodBodyBuilder<T, E>
		where T : class
		where E : Expression
	{
		internal MethodBodyBuilder(IMethodBuilder methodBuilder, MethodBodyContext context)
			: base(methodBuilder, context)
		{

		}

		public IMethodBodyBuilder<T, E> Statement(E statement)
		{
			this._body.Add(statement);
			return this;
		}

		public IMethodBodyBuilder<T, E> ReturnStatement(E statement)
		{
			this._returnStatement = statement;
			return this;
		}
	}
}
